#include "misc.h"
#include "color.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <vector>
#include <time.h>

#include "wininclude.h"


std::string sztolower(std::string input)
{
    unsigned int len = input.length();
    for(unsigned int i = 0; i < len; i++)
    {
        if( input[i] >= 'A' && input[i] <= 'Z' )
            input[i] = input[i] + 32; // ASCII difference by 32
    }

    return input;
}

std::string sztoupper(std::string input)
{
    unsigned int len = input.length();
    for(unsigned int i = 0; i < len; i++)
    {
        if( input[i] >= 'a' && input[i] <= 'z' )
        input[i] = input[i] - 32; // ASCII difference by 32
    }

    return input;
}

void cstrtolower(char *out, const char *in, size_t maxlen)
{
	unsigned int len = strlen(in);
	if( maxlen != 0 && len > (maxlen - 1) )
		len = maxlen - 1;

	char c;
	unsigned int i;
	for(i = 0; i < len; i++)
	{
		c = (char)(*(in + i));
		if( c >= 'A' && c <= 'Z' )
			out[i] = c + 32;
		else
			out[i] = c;
	}

	out[i] = '\0';
}

void cstrtoupper(const char *in, char *out, size_t maxlen)
{
	unsigned int len = strlen(in);
	if( maxlen != 0 && len > (maxlen - 1) )
		len = maxlen - 1;

	char c;
	unsigned int i;
	for(i = 0; i < len; i++)
	{
		c = (char)(*(in + i));
		if( c >= 'a' && c <= 'z' )
			out[i] = c - 32;
		else
			out[i] = c;
	}

	out[i] = '\0';
}

void color_printf(int color, const char *fmt, ...)
{
	va_list arglist;
	va_start(arglist, fmt);

	setColor(color);
	vprintf(fmt, arglist);
	setColor(COLOR_LIGHTGRAY);

	va_end(arglist);
}

void setColor(unsigned short int color, unsigned short int background)
{
	#ifdef USE_COLOR
	background *= 16;

	color |= background;
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
	#endif
}

/* Checks if lua+coco is available */
int cocoAvailable(lua_State *lstate)
{
/* Deprecated
	int coco_available = false;
	lua_getfield(lstate, LUA_GLOBALSINDEX, "coroutine");
	lua_getfield(lstate, -1, "coco");
	coco_available = lua_toboolean(lstate, -1);
	lua_pop(lstate, 2);

	return coco_available;
*/
	return false;
}

/* Callback function, for use in getAppHwnd() */
BOOL CALLBACK getAppHwndProc(HWND hwnd, LPARAM lparam)
{
    EnumHwndPair *pair = (EnumHwndPair *)lparam;
    DWORD Id;
    GetWindowThreadProcessId(hwnd, &Id);

    if(Id == pair->process)
    {
        pair->hwnd = hwnd;
        return false;
    }

    return true;
}

HWND getAppHwnd()
{
	return GetConsoleWindow();
/*
    static HWND foundHwnd = NULL;

    if( foundHwnd )
        return foundHwnd;

    EnumHwndPair pair;
    pair.hwnd = 0;
    pair.process = GetCurrentProcessId();

    EnumWindows(getAppHwndProc, (LPARAM)&pair);

    foundHwnd = pair.hwnd;
    return foundHwnd;
*/
}

int wildfind(const std::string &format, const std::string &checkstring)
{
  if( checkstring.length() == 0 || format.length() == 0 )
    return 0;

  unsigned int format_pos = 0;
  unsigned int checkstring_pos = 0;
  unsigned int mp = 0;
  unsigned int cp = 0;

  while( format.at(format_pos) != '*' && format.at(format_pos) != '?' )
  {
    if( format.at(format_pos) != checkstring.at(checkstring_pos) &&
      format.at(format_pos) != '?' && format.at(format_pos) != '*' )
      return 0;

    checkstring_pos++;
    format_pos++;

    if( checkstring_pos >= checkstring.length() || format_pos >= format.length() )
      break;
  }

  while( format_pos < format.length() && checkstring_pos < checkstring.length() )
  {
    if( format.at(format_pos) == '*' )
    {
      if( (format_pos < format.length()-1 && format.at(format_pos+1) != '*') ||
        format_pos == format.length()-1 )
      {
        format_pos++;
        if( format_pos >= format.length() )
          return 1;

        mp = format_pos;
        cp = checkstring_pos + 1;
      }
      else if( format.at(format_pos) == checkstring.at(checkstring_pos) )
      {
        format_pos++;
        checkstring_pos++;
      }
      else
      {
        format_pos = mp;
        checkstring_pos = cp++;
      }
    }
    else if( format.at(format_pos) == '?' )
    {
      if( format_pos < format.length()-1 && format.at(format_pos+1) == '?' )
      {
        if( format.at(format_pos) == checkstring.at(checkstring_pos) )
        {
          format_pos+=2;
          checkstring_pos++;
        }
        else
        {
          format_pos = mp;
          checkstring_pos = cp++;
        }
      }
      else
      {
        format_pos++;
        checkstring_pos++;
      }
    }
    else if( format.at(format_pos) == checkstring.at(checkstring_pos) )
    {
      format_pos++;
      checkstring_pos++;
    }
    else
    {
      format_pos = mp;
      checkstring_pos = cp++;
    }
  }

  while( format_pos < format.length() && format.at(format_pos) == '*' )
    format_pos++;

  if( format_pos >= format.length() )
    return true;
  else
    return false;
}


std::string getProcessorType()
{
    SYSTEM_INFO si;
    GetNativeSystemInfo(&si);

	int bufSize = 1024;
    char buffer[bufSize];
	char cpyBuffer[bufSize];
    strcpy((char*)buffer, "");

    if( si.dwNumberOfProcessors > 1)
        snprintf((char*)&buffer, 1024, "%uX ",
			(unsigned int)si.dwNumberOfProcessors);

	// We make a copy of the buffer so we aren't reading and
	// writing to the same buffer below
	strcpy((char*)cpyBuffer, (char*)buffer);

    switch( si.wProcessorArchitecture )
    {
        case PROCESSOR_ARCHITECTURE_AMD64:
            snprintf((char*)&buffer, bufSize, "%sAMD or Intel x64",
			(char*)&cpyBuffer);
        break;

        case PROCESSOR_ARCHITECTURE_IA64:
            snprintf((char*)&buffer, bufSize, "%sIntel Itanium x64",
			(char *)&cpyBuffer);
        break;

        case PROCESSOR_ARCHITECTURE_INTEL:
            snprintf((char*)&buffer, bufSize, "%s%u", (char*)&cpyBuffer,
                (unsigned int)si.dwProcessorType);
        break;

        case PROCESSOR_ARCHITECTURE_UNKNOWN:
            snprintf((char *)&buffer, bufSize,
				"%sPROCESSOR_ARCHITECTURE_UNKNOWN", (char*)&cpyBuffer);
        break;

        default:
            snprintf((char*)&buffer, bufSize, "%sUnknown", (char*)&cpyBuffer);
        break;
    }

    return std::string(buffer);
}

std::string getOsName()
{
    std::string servicepacksz = "";
    OSVERSIONINFOEX osvi;
	SYSTEM_INFO si;
	FUNC_GETNATIVESYSTEMINFO _getNativeSystemInfo; // (WOW64)

    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
	ZeroMemory(&si, sizeof(SYSTEM_INFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    GetVersionEx( (OSVERSIONINFO*)&osvi);

	_getNativeSystemInfo = (FUNC_GETNATIVESYSTEMINFO) GetProcAddress(
		GetModuleHandle("kernel32.dll"), "GetNativeSystemInfo");
	if( _getNativeSystemInfo )
		_getNativeSystemInfo(&si);
	else
		GetSystemInfo(&si);


    if( osvi.szCSDVersion ) {
        servicepacksz = " ";
        servicepacksz += osvi.szCSDVersion; }

    // version info from:
    // http://msdn2.microsoft.com/en-us/library/ms724834.aspx
    if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) // 4, 0
        return std::string("Windows 95") + servicepacksz;
    if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) // 4, 10
        return std::string("Windows 98") + servicepacksz;
    if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) // 4, 90
        return std::string("Windows ME") + servicepacksz;
    if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) // 5, 0
        return std::string("Windows 2000") + servicepacksz;
    if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) // 5, 1
        return std::string("Windows XP") + servicepacksz;
    if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) // 5, 2
	{
		if( (osvi.wProductType == VER_NT_WORKSTATION) &&
		(si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) )
			return std::string("Windows XP 64-bit") + servicepacksz;

		if( osvi.wSuiteMask == VER_SUITE_WH_SERVER )
			return std::string("Windows Home Server") + servicepacksz;

		if( GetSystemMetrics(SM_SERVERR2) == 0 )
			return std::string("Windows Server 2003") + servicepacksz;
		else
			return std::string("Windows Server 2003 R2") + servicepacksz;
	}
    if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0 ) // 6, 0
	{
		if( osvi.wProductType == VER_NT_WORKSTATION )
			return std::string("Windows Vista") + servicepacksz;
		else
			return std::string("Windows Server 2008") + servicepacksz;
	}
	if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1 ) // 6, 1
	{
		if( osvi.wProductType == VER_NT_WORKSTATION )
			return std::string("Windows 7") + servicepacksz;
		else
			return std::string("Windows Server 2008 R2") + servicepacksz;
	}

    // unknown OS
    char buffer[256];
    snprintf((char*)&buffer, 256, "Unknown Windows OS v%i.%i",
        (unsigned int)osvi.dwMajorVersion, (unsigned int)osvi.dwMinorVersion);
    return std::string(buffer);
}

void clearCliScreen()
{
    HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {0, 0};
    DWORD count;

    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(stdOut, &csbi);

    FillConsoleOutputCharacter(stdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y,
        coord, &count);

    SetConsoleCursorPosition(stdOut, coord);
}

std::string getSystemError(int err)
{
    char *tmp = NULL;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, err, 0, (CHAR*)&tmp, 0, NULL);

    if( tmp == NULL )
        return "";

    std::string retval = tmp;
    LocalFree(tmp);

    retval.erase( retval.size()-2 ); // chop off trailing \r\n

    return retval;
}

/* Converts a wide string to a "regular" string.
   Non-convertable characters (ie. > 255), will
   be replaced with spaces.
*/
std::string narrowString(std::wstring instr)
{
  std::string holder(instr.begin(), instr.end());
  return holder;
}


/* Generates a random string of length 'len', and
	with the attributes specified in flags.
*/
std::string randomString(unsigned int len, unsigned int flags)
{
	static bool seeded = false;
	std::vector<char> allowedChars;
	std::string holder;
	char i = 0;

	if( !seeded ) {
		seeded = true;
		srand(time(NULL));
	}

	if( flags == 0 )
		flags = RS_LETTERS;

	if( flags & RS_LETTERS ) {
		for(i = 'a'; i < 'z'; i++)
			allowedChars.push_back(i);

		for(i = 'A'; i < 'Z'; i++)
			allowedChars.push_back(i);
	}

	if( flags & RS_NUMBERS ) {
		for(i = '0'; i < '9'; i++)
			allowedChars.push_back(i);
	}

	if( flags & RS_SPECIAL ) {
		allowedChars.push_back('#');
		allowedChars.push_back('!');
		allowedChars.push_back('*');
		allowedChars.push_back('.');
		allowedChars.push_back('?');
		allowedChars.push_back('@');
		allowedChars.push_back('_');
	}

	for(unsigned j = 0; j < len; j++)
	{
		int index = (rand() * 0xC7F6A4F1) % allowedChars.size();
		holder.push_back( allowedChars.at(index) );
	}

	return holder;
}

void rot13(char *str)
{
	unsigned int len = strlen(str);
	for(unsigned int i = 0; i < len; i++)
	{
		if( (str[i] >= 'a' && str[i] <= 'z') ||
			(str[i] >= 'A' && str[i] <= 'Z') )
		{
			// 'a' is after 'Z', so we can use this
			// to check if it's upper or lower case.
			// We already know it is A-Z or a-z, so
			// it is safe to make this assumption.
			if( str[i] >= 'a' )
				str[i] = 'a' + (str[i] - 'a' + 13) % 26;
			else
				str[i] = 'A' + (str[i] - 'A' + 13) % 26;
		}
	}
}

void initCliOptions(CLI_OPTIONS *clio)
{
	clio->wineMode = false;
	clio->scriptSet = false;
}

void initConfigOptions(CONFIG_OPTIONS *co)
{
    co->language = "";
    co->keyboard = "";
    co->windowWidth = 0;
    co->windowHeight = 0;
    co->flashOnError = true;
    co->allowSystemCommands = true;
    co->useStealth = false;
    co->enableSound = true;
}

void handleCommandLineArguments(unsigned int argc, char ** argv,
CLI_OPTIONS *pclio)
{
	initCliOptions(pclio);

	bool switchesFinished = false;

	for(unsigned int i = 1; i < argc; i++)
	{
		if( !switchesFinished && strncmp(argv[i], "--", 2) == 0 )
		{ // -- switch
			if( strcmp( (char *)&argv[i][2], "wine") == 0 )
				pclio->wineMode = true;

			continue;
		}

		if( !switchesFinished && argv[i][0] == '-' )
		{ // - switch
			unsigned int len = strlen(argv[i]);
			for(unsigned int j = 1; j < len; j++)
			{
				if( argv[i][j] == 'w' )
					pclio->wineMode = true;
			}

			continue;
		}

		switchesFinished = true;

		if( !pclio->scriptSet )
		{ // If no script name has been set,
		  // this must be a script name (not args)
			pclio->scriptName = argv[i];
			pclio->scriptSet = true;
		}
		else
		{ // Must be arguments.
			pclio->args.push_back(argv[i]);
		}
	}
}

DWORD getUserPriv()
{
	LPUSER_INFO_1 ui1 = NULL;
	wchar_t username[UNLEN+1];
	DWORD usernameSize = UNLEN + 1;
	NET_API_STATUS status;

	GetUserNameW((WCHAR *)&username, &usernameSize);
	status = NetUserGetInfo(NULL, (WCHAR *)&username, (DWORD)1, (LPBYTE *)&ui1);

	if( ui1 == NULL )
	{ /* If some error occurs, assume guest */
		return USER_PRIV_GUEST;
	}

	return ui1->usri1_priv;
}

std::string getPrivName(DWORD priv)
{
	switch(priv)
	{
		case USER_PRIV_GUEST:
			return "Guest";
		break;

		case USER_PRIV_USER:
			return "User";
		break;

		case USER_PRIV_ADMIN:
			return "Administrator";
		break;

		default:
			return "Unknown";
		break;
	}
}
